#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

const std::string gpathname("/home/xiaoqian/git_clone/xiaoqian-Linux/2024-04-27上_命名管道+共享内存/shm");
const int gproj_id = 0x66;
// 身份
const int gCreater = 1;
const int gUser = 2;

const int gShmSize = 4096;

// 封装
class Shm
{
    int GetShmHelper(int flag) // 方式
    {
        _size = gShmSize;
        _shmid = shmget(_key, _size, flag);
        if (_shmid < 0) //(shmid == -1) //返回OS生成的共享内存唯一性标识符
        {
            perror("shmget fail");
            exit(-1);
        }
        return _shmid;
    }
    void Attach() // 挂接
    {
        // sleep(5);
        if (_shmaddr) // 挂接前先清空_shmaddr之前的挂接
            Detach(), _shmaddr = nullptr;
        _shmaddr = shmat(_shmid, nullptr, 0);
        if (_shmaddr == (void *)-1)
        {
            perror("shmat fail");
            return;
        }
        else
        {
            std::cout << RoleToString() << " attach success" << std::endl;
        }
    }
    void Detach() // 取消挂接
    {
        // sleep(5);
        if (_shmaddr == nullptr)
            return;
        int n = shmdt(_shmaddr);
        if (n < 0)
        {
            perror("shmdt fail");
            return;
        }
        else
        {
            std::cout << "shmdt success" << std::endl;
        }
    }
    std::string ToOct(int num) //转成八进制
    {
        char buf[128] = {0};
        snprintf(buf, sizeof(buf), "0%o", num);
        return buf;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _shmaddr(nullptr)
    {
        GetCommKey();
        if (_who == gCreater)
        {
            GetShmUseCreate();
        }
        else if (_who == gUser)
        {
            GetShmForUser();
        }
        std::cout << "shmid:" << _shmid << std::endl;
        std::cout << "key:" << KeyToHex(_key) << std::endl;
        Attach();
    }
    ~Shm()
    {
        if (_shmaddr) // != nullptr
            Detach();
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr); // 删除
            if (res >= 0)
            {
                std::cout << "shm remove done..." << std::endl;
            }
        }
    }
    std::string KeyToHex(key_t key) // 把key转成十六进制打印显示(因为系统显示key时是以16进制)
    {
        char buffer[128] = {0};
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    void GetCommKey()
    {
        _key = ftok(_pathname.c_str(), _proj_id);
        if (_key == -1)
        {
            perror("ftok fail");
            exit(-1);
        }
    }
    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            int res = GetShmHelper(IPC_CREAT | IPC_EXCL | 0666);
            if (res >= 0)
            {
                std::cout << "shm create done..." << std::endl;
                return true;
            }
        }
        return false;
    }
    bool GetShmForUser()
    {
        if (_who == gUser)
        {
            int res = GetShmHelper(IPC_CREAT | 0666);
            if (res >= 0)
            {
                std::cout << "shm get done..." << std::endl;
                return true;
            }
        }
        return false;
    }
    std::string RoleToString()
    {
        if (_who == gCreater)
            return "gCreater";
        else
            (_who = gUser);
        return "gUser";
    }

    void *Addr()
    {
        return _shmaddr;
    }
    void Zero()
    {
        if (_shmaddr)
            memset(_shmaddr, 0, _size);
    }

    void TestState()
    {
        shmid_ds buf;
        int n = shmctl(_shmid, IPC_STAT, &buf);
        if (n < 0)
        {
            perror("shmctl fail");
            return;
        }
        std::cout << "buf.shm_perm.__key: " << KeyToHex(buf.shm_perm.__key) << std::endl;
        std::cout << "buf.shm_perm.mode: " << ToOct(buf.shm_perm.mode) << std::endl;
        std::cout << "buf.shm_nattch: " << buf.shm_nattch << std::endl;
    }

private:
    std::string _pathname;
    int _proj_id;
    key_t _key;
    int _shmid;
    int _size;
    int _who; // 身份
    void *_shmaddr;
};

// std::string ToHex(key_t key) // 把key转成十六进制打印显示(因为系统显示key时是以16进制)
// {
//     char buffer[128] = {0};
//     snprintf(buffer, sizeof(buffer), "0x%x", key);
//     return buffer;
// }

// key_t GetCommKey(const std::string& pathname, int proj_id)
// {
//     key_t key = ftok(pathname.c_str(), proj_id);
//     if (key == -1)
//     {
//         perror("ftok fail");
//         exit(-1);
//     }
//     return key;
// }

// int ShmGet(key_t key, size_t size)
// {
//     int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL);
//     if (shmid < 0)    //(shmid == -1) //返回OS生成的共享内存唯一性标识符
//     {
//         perror("shmget fail");
//         exit(-1);
//     }
//     return shmid;
// }